package com.maimao.goods.service;

import cn.hutool.core.util.ObjectUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.collect.Lists;
import com.maimao.core.constant.Constants;
import com.maimao.core.constant.RedisKeys;
import com.maimao.core.exception.MaiMaoDaoException;
import com.maimao.goods.mapper.SpecDetailMapper;
import com.maimao.goods.mapper.SpecOptionMapper;
import com.maimao.model.dynamicform.DynamicForm;
import com.maimao.model.dynamicform.MultiValueDynamicForm;
import com.maimao.model.dynamicform.Options;
import com.maimao.model.dynamicform.SingleValueDynamicForm;
import com.maimao.model.dynamicform.props.DynamicAddProps;
import com.maimao.model.dynamicform.props.InputNumberProps;
import com.maimao.model.dynamicform.props.InputProps;
import com.maimao.model.dynamicform.props.SelectProps;
import com.maimao.model.dynamicform.validate.DynamicAddValidate;
import com.maimao.model.dynamicform.validate.NumberValidate;
import com.maimao.model.dynamicform.validate.Validate;
import com.maimao.model.dynamicform.vo.SpecDynamicFormVo;
import com.maimao.model.goods.SpecDetails;
import com.maimao.model.goods.SpecOptions;
import com.maimao.model.goods.enums.FormType;
import com.maimao.model.goods.input.SpecDetailForm;
import com.maimao.model.goods.vo.SpecDetailVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author MaoLin Wang
 * @date 2020/12/11 8:18 下午
 */
@Service
public class SpecDetailService {

    private Logger log = LoggerFactory.getLogger(SpecDetailService.class);
    @Autowired
    private SpecDetailMapper specDetailMapper;

    @Autowired
    private SpecOptionMapper specOptionMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private Cache<Integer, SpecDetails> specCache;


    @PostConstruct
    public void initAll() {
        List<SpecDetails> specDetails = specDetailMapper.selectAll();
        if (ObjectUtil.isNotEmpty(specDetails)) {
            Map<Integer, SpecDetails> map = specDetails.stream().collect(Collectors.toMap(SpecDetails::getId, c -> c));
            specCache.putAll(map);
        }

    }

    public SpecDetails selectByIdCache(Integer id) {
        return specCache.get(id, id1 -> specDetailMapper.selectById(id1));
    }

    public List<SpecDetails> getSpecDetailByCategoryId(Integer cid3) {
        List<SpecDetails> specDetails = (List<SpecDetails>) redisTemplate.opsForHash().get(RedisKeys.SPEC_DETAIL, cid3);
        if (specDetails == null || specDetails.size() <= 0) {
            List<SpecDetails> specDetailsList = specDetailMapper.selectAll();
            if (specDetailsList != null && specDetailsList.size() > 0) {
                Map<Integer, List<SpecDetails>> cateGroup = specDetailsList.stream().collect(Collectors.groupingBy(SpecDetails::getCategoryId));
                redisTemplate.opsForHash().putAll(RedisKeys.SPEC_DETAIL, cateGroup);
                return (List<SpecDetails>) redisTemplate.opsForHash().get(RedisKeys.SPEC_DETAIL, cid3);
            }
            return null;
        }
        return specDetails;
    }

    /**
     * 根据规格组查询细分规格
     */
    public List<SpecDetailVo> selectDetailByGroupId(Integer groupId) {
        List<SpecDetailVo> specDetailVos = specDetailMapper.selectDetailExByGroupId(groupId);
        return specDetailVos;
    }

    /**
     * 修改规格细分
     *
     * @param specDetailForm
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void update(SpecDetailForm specDetailForm) {
        specDetailMapper.updateCus(specDetailForm.toSpecDetails());
        Integer detailId = specDetailForm.getId();
        List<String> specOptions = specDetailForm.getSpecOptions();
        specOptionMapper.deleteByDetaiId(detailId);

        if (specOptions != null && specOptions.size() > 0) {
            specOptionMapper.insertBatch(detailId, specOptions);
        }

    }

    /**
     * 添加规格细分
     *
     * @param specDetailForm
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void add(SpecDetailForm specDetailForm) {
        SpecDetails specDetails = specDetailForm.toSpecDetails();
        int count = specDetailMapper.insert(specDetails);
        if (count > 0) {
            Integer detailId = specDetails.getId();
            List<String> specOptions = specDetailForm.getSpecOptions();
            if (specOptions != null && specOptions.size() > 0) {
                specOptionMapper.insertBatch(detailId, specOptions);
            }
        }

    }

    /**
     * 根据三级分类获取规格动态表单项
     */
    public SpecDynamicFormVo loadDynamicForm(Integer categoryId) {
        List<SpecDetailVo> specDetailVoList = specDetailMapper.selectDetailByCid(categoryId);
        Map<Integer, List<SpecDetailVo>> commonGroup =
                specDetailVoList.stream().collect(Collectors.groupingBy(SpecDetailVo::getCommon));
        List<SpecDetailVo> commonSpecList = commonGroup.get(Constants.ONE);
        List<SpecDetailVo> sellSpecList = commonGroup.get(Constants.ZERO);

        List<DynamicForm> commonSpecForms = null;
        List<DynamicForm> sellSpecForms = null;

        //根据JSON动态生成表单例子: http://jsrun.net/NQhKp/edit
        if (commonSpecList != null && commonSpecList.size() > 0) {
            commonSpecForms = new ArrayList<>(commonSpecList.size());

            DynamicForm dynamicForm = null;
            for (SpecDetailVo spec : commonSpecList) {
                String formType = spec.getFormType();
                if (StringUtils.isEmpty(formType)) {
                    log.error("规格项表单类型为空{}", spec);
                    continue;
                }
                if (formType.equals(FormType.MULTI_SELECT.getName())) {
                    dynamicForm = new MultiValueDynamicForm();
                } else {
                    dynamicForm = new SingleValueDynamicForm();
                }
                dynamicForm.setField(spec.getId() + "");
                String unit = spec.getUnit();
                if (!StringUtils.isEmpty(unit)) {
                    dynamicForm.setTitle(spec.getName() + "(" + unit + ")");
                } else {
                    dynamicForm.setTitle(spec.getName());
                }


                getDynamicForm(spec, formType, commonSpecForms, dynamicForm);

            }

        }
        //动态添加表单例子: http://jsrun.net/VLfKp/edit
        if (sellSpecList != null && sellSpecList.size() > 0) {
            sellSpecForms = new ArrayList<>(sellSpecList.size());

            DynamicForm dynamicForm = null;
            for (int index = 0; index < sellSpecList.size(); index++) {
                SpecDetailVo spec = sellSpecList.get(index);
                dynamicForm = new DynamicForm();
                dynamicForm.setType(FormType.GROUP.getName());
                String unit = spec.getUnit();
                if (!StringUtils.isEmpty(unit)) {
                    dynamicForm.setTitle(spec.getName() + "(" + unit + ")");
                } else {
                    dynamicForm.setTitle(spec.getName());

                }
                dynamicForm.setField(spec.getId() + "");

                String formType = spec.getFormType();
                if (StringUtils.isEmpty(formType)) {
                    log.error("规格项表单类型为空{}", spec);

                    continue;
                }
                DynamicAddProps props = new DynamicAddProps();

                List<DynamicForm> rules = new ArrayList<>(1);
                DynamicForm form = null;
                if (formType.equals(FormType.MULTI_SELECT.getName())) {
                    form = new MultiValueDynamicForm();
                } else {
                    form = new SingleValueDynamicForm();
                }

                form.setTitle("可选值");
                form.setField(spec.getId() + "");

                getDynamicForm(spec, formType, rules, form);
                props.setRules(rules);

                dynamicForm.setValidate(Lists.newArrayList(new DynamicAddValidate()));
                dynamicForm.setProps(props);

                sellSpecForms.add(dynamicForm);
            }
        }


        SpecDynamicFormVo vo = new SpecDynamicFormVo();
        if (commonSpecForms != null) {
            vo.setCommonSpecs(commonSpecForms);
        }
        if (sellSpecForms != null) {
            vo.setSellSpecs(sellSpecForms);
        }
        return vo;
    }


    private void getDynamicForm(SpecDetailVo spec, String formType, List<DynamicForm> dynamicFormList, DynamicForm form) {
        if (formType.equals(FormType.TEXT.getName())) {
            form.setType(FormType.INPUT.getName());
            form.setValidate(Validate.getDefaultValidate(spec));
            form.setProps(new InputProps(spec.getName()));
            dynamicFormList.add(form);

        } else if (formType.equals(FormType.NUMBER.getName())) {
            form.setType(FormType.INPUT_NUMBER.getName());
            form.setValidate(NumberValidate.getDefaultNumberValidates());
            form.setProps(new InputNumberProps());
            dynamicFormList.add(form);

        } else if (formType.equals(FormType.SELECT.getName()) || formType.equals(FormType.MULTI_SELECT.getName())) {
            if (formType.equals(FormType.MULTI_SELECT.getName())) {
                ((MultiValueDynamicForm) form).setValue(Lists.newArrayList());
            }
            form.setType(FormType.SELECT.getName());
            form.setValidate(Validate.getSelectValidates(spec, formType));
            form.setProps(SelectProps.getDefaultSelectProps(spec, formType));
            form.setOptions(getOptions(spec.getSpecOptions()));
            dynamicFormList.add(form);
        }
    }

    private List<Options> getOptions(List<SpecOptions> specOptions) {
        List<Options> options = new ArrayList<>(specOptions.size());
        Options option = null;
        for (SpecOptions specOption : specOptions) {
            option = new Options();
            option.setLabel(specOption.getOption());
            option.setValue(specOption.getOption());
            options.add(option);
        }
        return options;
    }

    public List<SpecDetails> findSearchSpecByCid(Integer categoryId) {
        return specDetailMapper.selectSearchableByCid(categoryId);
    }

    public List<SpecDetails> findSearchSpecByCidList(List<Integer> categoryIdList) {
        return specDetailMapper.selectSearchableByCidList(categoryIdList);
    }
}
